/*******************************************************
 * spi_module.v
 * 
 * SPI模块
 *                      ______________
 *                clk -|              |- CS
 *                rst -|              |- MCLK
 *       data_in[n:0] -|              |- MOSI
 *      data_out[n:0] -|              |- MISO
 *           opt_comp -|              |
 *          start_opt -|              |
 *            cs_ctrl -|              |
 *      freq_div[n:0] -|              |
 *          mode[1:0] -|              |
 *           bit_ctrl -|              |
 *                     |______________|
 *******************************************************/


module spi_module #(
    parameter DATA_WIDTH = 8,
    parameter DIVIDER_WIDTH = 32)(
    /* 通用控制信号 */
    input clk, // 时钟
    input rst, // 复位

    /* 模块控制信号 */
    input [DATA_WIDTH:0] data_in, // 数据输入
    output reg [DATA_WIDTH:0] data_out, // 数据输出
    input start_transmit, // 开始传输
    output reg transmit_complete, // 传输完成
    input cs_ctrl, // 片选控制
    input [DIVIDER_WIDTH:0] freq_div, // 分频系数
    input [1:0] mode,

    /* 模块输入输出信号 */
    output reg cs, // 片选输出
    output reg mclk, // 时钟输出
    output reg mosi, // 串行输出
    input miso // 串行输入
);

    // reg cs;

    /*******************************************************
     * CS控制
     *******************************************************/
    always @(posedge clk or negedge rst) begin
        if(!rst) begin
            // 复位时，CS拉高
            cs <= 1'b1;
        end else begin
            if(cs_ctrl) begin
                cs <= 1'b0;
            end else begin
                cs <= 1'b1;
            end
        end
    end
    
    /*******************************************************
     * MCLK生成
     *******************************************************/
    reg [DIVIDER_WIDTH:0] divider;  // 分频计数器
    reg internal_mclk;  // 内部MCLK

    /* 计数分频,产生内部时钟 */
    always @(posedge clk or negedge rst) begin
        if(!rst) begin
            divider <= 0;
        end else begin
            if(start_transmit) begin
                if(divider < freq_div) begin
                    divider <= divider + 1'b1;
                end else begin
                    divider <= 1'b1;
                    internal_mclk <= ~internal_mclk;
                end
            end else begin
                divider <= 1'b0;
                internal_mclk <= 1'b1;
            end
        end
    end

    /* 按照模式不同，产生MCLK */
    always @(posedge clk or negedge rst) begin
        if(!rst) begin
            mclk <= 0;
        end else begin
            if(start_transmit) begin
                // CPOL = 1, CHHA = 0 or CPOL = 0, CHHA = 1
                if(mode[0] ^ mode[1]) begin
                    mclk <= internal_mclk;
                end else begin  // CPOL = 0, CHHA = 0 or CPOL = 1, CHHA = 1
                    mclk <= ~internal_mclk;
                end
            end else begin
                if(mode[1]) begin
                    mclk <= 1'b1;
                end else begin
                    mclk <= 1'b0;
                end
            end
        end
    end
    
    /*******************************************************
     * 数据输入输出
     *******************************************************/
    reg [5:0] data_counter;
    always @(posedge internal_mclk or negedge rst) begin
        if(!rst) begin
            data_counter <= 1'b0;
            transmit_complete <= 1'b0;
        end else if(!start_transmit) begin
            data_counter <= 1'b0;
            transmit_complete <= 1'b0;
        end else begin
            if(data_counter < DATA_WIDTH) begin
                data_counter <= data_counter + 1'b1;
                transmit_complete <= 1'b0;
            end else begin
                data_counter <= 0;
                transmit_complete <= 1'b1;
            end
        end
    end

    // 数据输出
    always @(posedge clk or negedge rst) begin
        if(!rst) begin
            mosi <= 1'b0;
        end else if(!start_transmit) begin
            mosi <= 1'b0;
        end else if(transmit_complete) begin
            mosi <= 1'b0;
        end else begin
            mosi <= data_in[data_counter];
        end
    end

    // 数据输入
    always @(negedge internal_mclk or negedge rst) begin
        if(!rst) begin
            data_out <= 0;
        end else if(!start_transmit) begin
            data_out <= 0;
        end else if(transmit_complete) begin
            data_out <= data_out;
        end else begin
            data_out[data_counter] <= miso;
        end
    end

endmodule



